import { useGameStore } from '@/stores/modules/game'
import { usePlayerStore } from '@/stores/modules/player'
import Worker from './worker?worker'
import * as _ from 'lodash-es'
import { getBestCombResult } from './worker-mixin'
import { useRestaurantStore } from '@/stores/modules/restaurant'
import { calcPriceBonusFromSkill } from '@/utils/effect'

import type { IChef, IRecipe } from '@/stores/types'
import type {
  IKitchen,
  ICookableRecipe,
  IChefCookableRecipes,
  IBestCombResult,
  IWorkerOutputMessage,
  ISettings,
} from './types'

export const getKitchenTeams = ({ teams, settings }: IKitchen) => {
  const teamChefCookableRecipes = teams.map((team) =>
    getChefCookableRecipes(team.chefIds, team.recipeIds, settings),
  )

  console.log(teamChefCookableRecipes)

  const chefRecipeCombinations = generateChefRecipeCombinations(teamChefCookableRecipes, settings)

  return assignCombinationsToWorker(chefRecipeCombinations, settings)
}

/**
 * 厨师制作菜谱的技法倍数
 * @param chef 厨师
 * @param recipe 菜谱
 * @returns 返回可以制作的技法倍数，大于等于 1 为可以制作
 */
function getTechniqueMultiple(chef: IChef, recipe: IRecipe) {
  const COOKING_TYPE = ['boil', 'knife', 'bake', 'fry', 'steam', 'stirfry'] as const
  const skillMultiples: number[] = []

  for (const skill of COOKING_TYPE) {
    if (recipe[skill] > 0) {
      const multiple = Math.floor(chef[skill] / recipe[skill])
      skillMultiples.push(multiple)
    }
  }

  const minMultiple = skillMultiples.length ? Math.min(...skillMultiples) : 0
  return minMultiple
}

/**
 * 计算品质加成
 */
function calculateQualityBonus(chef: IChef, recipe: IRecipe) {
  const minMultiple = getTechniqueMultiple(chef, recipe)

  if (minMultiple < 1) {
    throw new Error(`厨师${chef}无法制作菜谱${recipe}`)
  }

  if (minMultiple >= 5) return 1
  if (minMultiple >= 4) return 0.5
  if (minMultiple >= 3) return 0.3
  if (minMultiple >= 2) return 0.1
  return 0
}

/**
 * 计算单份菜谱的最终价格
 * @param chef 厨师
 * @param recipe 菜谱
 * @returns 单份菜谱的最终价格
 */
function calculateSingleRecipePrice(chef: IChef, recipe: IRecipe) {
  const qualityBonus = calculateQualityBonus(chef, recipe)
  const skillBonus = calcPriceBonusFromSkill(chef, recipe)
  const isRecipeEx = usePlayerStore().isRecipeEx(recipe.recipeId)
  const exPrice = isRecipeEx ? recipe.exPrice : 0
  const decoBuff = usePlayerStore().decoBuff

  return Math.ceil((recipe.price + exPrice) * (1 + skillBonus + qualityBonus + decoBuff))
}

/**
 * 计算一组菜谱的总收益
 * @param chef 厨师
 * @param recipe 菜谱
 * @returns 一组菜谱的总收益
 */
function calculateBatchRevenue(chef: IChef, recipe: IRecipe) {
  const singlePrice = calculateSingleRecipePrice(chef, recipe)
  return singlePrice * recipe.limit
}

/**
 * 计算一组菜谱所需时间
 * @param recipe 菜谱
 * @returns 一组菜谱所需时间（秒）
 */
function calculateBatchTime(recipe: IRecipe) {
  return recipe.time * recipe.limit
}

/**
 * 辅助函数：生成数组的组合 (Combination)
 * @param arr 原始数组
 * @param k 组合大小
 * @returns 所有组合
 */
function getCombinations<T>(arr: T[], k: number) {
  const result: T[][] = []

  backtrack([], 0)

  return result

  function backtrack(currentCombo: T[], start: number) {
    if (currentCombo.length === k) {
      result.push([...currentCombo])
      return
    }
    for (let i = start; i < arr.length; i++) {
      currentCombo.push(arr[i])
      backtrack(currentCombo, i + 1)
      currentCombo.pop()
    }
  }
}

/**
 * 获取厨师能做的菜谱
 * @param chefIds 厨师id
 * @param recipeIds 菜谱id
 * @filterRule 排序规则
 * @returns 返回对应数据
 */
function getChefCookableRecipes(
  chefIds: number[],
  recipeIds: number[],
  { sortRule, techniques }: ISettings,
) {
  const { chefsMap, recipesMap } = useGameStore()
  const chefCookableRecipes: IChefCookableRecipes[] = []

  chefIds.forEach((chefId) => {
    const cookableRecipes: ICookableRecipe[] = []
    const chef = chefsMap.get(chefId)
    if (!chef) return
    recipeIds.forEach((recipeId) => {
      const recipe = recipesMap.get(recipeId)
      if (!recipe) return
      if (getTechniqueMultiple(chef, recipe) >= techniques) {
        const batchRevenue = calculateBatchRevenue(chef, recipe)
        const batchTime = calculateBatchTime(recipe)
        if (batchTime > 0) {
          // 避免除以零
          const efficiency = batchRevenue / batchTime
          cookableRecipes.push({
            recipeId,
            efficiency: efficiency,
            batchRevenue: batchRevenue,
            batchTime: batchTime,
          })
        }
      }
    })

    if (sortRule === 'MAX_REVENUE') {
      // 按收益效率（收益/时间）排序，效率相同选取总收益最高
      cookableRecipes.sort((a, b) => {
        if (b.efficiency === a.efficiency) {
          return b.batchRevenue - a.batchRevenue
        }
        return b.efficiency - a.efficiency
      })
    } else if (sortRule === 'MIN_TIME') {
      // 按最短时间排序，时间相同选取总收益最高
      cookableRecipes.sort((a, b) => {
        if (b.batchTime === a.batchTime) {
          return b.batchRevenue - a.batchRevenue
        }
        return a.batchTime - b.batchTime
      })
    }

    // 剪枝，每个厨师最多只能做3份菜谱，所以只需要考虑前9个效率最高的菜谱
    if (cookableRecipes.length > 9) {
      cookableRecipes.length = 9
    }

    chefCookableRecipes.push({
      chefId,
      cookable: cookableRecipes,
    })
  })

  return chefCookableRecipes
}

/**
 * 生成厨师组合
 * @param chefArrays 包含所有厨师备选数组的数组
 * @param combSize 组合中厨师的数量
 * @returns 返回厨师组合
 */
function generateChefRecipeCombinations(chefArrays: IChefCookableRecipes[][], settings: ISettings) {
  const combSize = settings.maxChefCount
  const combinations: IChefCookableRecipes[][] = []
  const chefCombSets = new Set<string>()

  /**
   * 递归
   * @param currentComb 当前厨师组合
   * @param startIndex 从某个备选厨师组合开始
   * @param depth 当前厨师组合上限
   */
  function generate(currentComb: IChefCookableRecipes[], startIndex: number, depth: number) {
    if (depth === combSize) {
      const currentChefIds = currentComb.map((chef) => chef.chefId).sort()
      const combinationKey = currentChefIds.join(',')

      if (!chefCombSets.has(combinationKey)) {
        combinations.push([...currentComb])
        chefCombSets.add(combinationKey)
      }

      return
    }

    if (startIndex >= chefArrays.length) {
      return
    }

    const currentArray = chefArrays[startIndex]

    for (const chef of currentArray) {
      // 检查当前厨师是否已在当前组合中
      const isChefInCombination = currentComb.some((c) => c.chefId === chef.chefId)

      if (!isChefInCombination) {
        currentComb.push(chef)
        generate(currentComb, startIndex + 1, depth + 1)
        currentComb.pop() // 回溯
      }
    }
  }

  // 初始调用
  generate([], 0, 0)

  return combinations
}

/**
 * 厨师组合分配给worker来计算
 * @param chefCombinations 厨师组合
 * @param settings
 */
async function assignCombinationsToWorker(
  chefCombinations: IChefCookableRecipes[][],
  settings: ISettings,
) {
  let bestCombResult: IBestCombResult = {
    totalRevenue: 0,
    workingTime: 0,
    combination: [],
  }

  let progressCount = 0
  const workerPromises: Promise<void>[] = []
  const { setWorkerProgress } = useRestaurantStore()

  setWorkerProgress(0)

  console.time('worker耗时')
  const chefComb = splitCombinations(chefCombinations, settings.threads)
  chefComb.forEach((comb, index) => {
    const myWorker = new Worker()
    const workerPromise = new Promise<void>((resolve, reject) => {
      myWorker.onmessage = workerOnmessage
      myWorker.onerror = workerOnerror

      function workerOnmessage(event: MessageEvent<IWorkerOutputMessage>) {
        const data = event.data
        if (data.type === 'end') {
          bestCombResult = getBestCombResult(data.bestCombResult, bestCombResult, settings)

          console.log(`从 Worker${index + 1} 接收到结果:`, bestCombResult)
          myWorker.terminate()
          resolve()
        } else if (data.type === 'progress') {
          progressCount += data.progressStep
          const percentage = Math.floor((progressCount / chefCombinations.length) * 100)
          setWorkerProgress(percentage)
        }
      }

      function workerOnerror(error: ErrorEvent) {
        console.error(`Worker${index + 1} 发生错误:`, error)
        myWorker.terminate()
        reject(error)
      }
    })

    workerPromises.push(workerPromise)

    console.log('分配的组合数：', comb.length)

    myWorker.postMessage({ type: 'start', chefCombinations: comb, settings })

    console.log(`向 Worker${index + 1} 发送了计算请求。`)
  })

  try {
    await Promise.all(workerPromises)
    console.warn('所有 Worker 已处理完毕。')
  } catch (error) {
    console.error('部分 Worker 发生错误：', error)
  } finally {
    console.timeEnd('worker耗时')
  }

  return bestCombResult
}

/**
 * 辅助函数，根据大小切割数组
 * @param arr 原始数组
 * @param k 切割大小
 * @returns 切割好的数组
 */
function splitCombinations<T>(arr: T[], k: number) {
  if (k <= 0) return [] // 处理k为0或负数的情况
  if (k >= arr.length) return arr.map((item) => [item]) // 处理k大于或等于数组长度的情况，每个元素一个数组

  const result: T[][] = []
  let currentIdx = 0

  for (let i = 0; i < k; i++) {
    const remainingElements = arr.length - currentIdx
    const remainingBuckets = k - i

    // 计算当前桶应该有多少元素
    const count = Math.ceil(remainingElements / remainingBuckets)
    result.push(arr.slice(currentIdx, currentIdx + count))
    currentIdx += count
  }

  return result
}
